package net.bithappens.hnqe.impl.rest.handler;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.GregorianCalendar;
import java.util.List;

import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;

import net.bithappens.hnqe.InvalidDataException;
import net.bithappens.hnqe.InvalidInstanceException;
import net.bithappens.hnqe.graph.GraphElement;
import net.bithappens.hnqe.graph.GraphNode;
import net.bithappens.hnqe.graph.GraphRelation;
import net.bithappens.hnqe.graph.GraphRelationMember;
import net.bithappens.hnqe.impl.rest.provider.CalendarJsonAddapter;
import net.bithappens.hnqe.impl.rest.provider.GraphElementJsonAddapter;
import net.bithappens.hnqe.impl.rest.provider.GraphRelationMemberJsonAddapter;
import net.bithappesn.hnqe.traverse.GraphPath;

import org.apache.log4j.Logger;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonSyntaxException;


@Path("/hnqe/traverse")
public class RestTraverse extends RootResource{

	private Logger log = Logger.getLogger(this.getClass());
	
	@Context 
	private UriInfo uriInfo;
	
	
	@GET
	@Path("/node/{instanceId}/{nodeUri}")
	@Produces({"application/json"})
	public Response getNodeByUri(
			@PathParam("instanceId") String instanceId, 
			@PathParam("nodeUri") String nodeUri) throws InvalidDataException, URISyntaxException, InvalidInstanceException{
		
		GraphNode node = hnqe.getCachTraverseInterface().getNodeByUri(instanceId, new URI(nodeUri));
		
		if(node == null){
			return Response.noContent().build();
		} else {
			Gson gson = new GsonBuilder()
				.registerTypeAdapter(GraphRelationMember.class, new GraphRelationMemberJsonAddapter())
				.registerTypeAdapter(GraphElement.class, new GraphElementJsonAddapter())
				.registerTypeAdapter(Calendar.class, new CalendarJsonAddapter())
				.registerTypeAdapter(GregorianCalendar.class, new CalendarJsonAddapter())
				.create();
			
			return Response.ok(gson.toJson(node), "application/json").build();	
		}
		
	}
		
	@GET
	@Path("/relation/{instanceId}/{relationUri}")
	@Produces({"application/json"})
	public Response getRelationByUri(
			@PathParam("instanceId") String instanceId, 
			@PathParam("relationUri") String relationUri) throws InvalidDataException, URISyntaxException, InvalidInstanceException{
		
		GraphRelation relation = hnqe.getCachTraverseInterface().getRelationByUri(instanceId, new URI(relationUri));
		
		if(relation == null){
			return Response.noContent().build();
		} else {
			Gson gson = new GsonBuilder()
				.registerTypeAdapter(GraphRelationMember.class, new GraphRelationMemberJsonAddapter())
				.registerTypeAdapter(GraphElement.class, new GraphElementJsonAddapter())
				.registerTypeAdapter(Calendar.class, new CalendarJsonAddapter())
				.registerTypeAdapter(GregorianCalendar.class, new CalendarJsonAddapter())
				.create();
			return Response.ok(gson.toJson(relation), "application/json").build();	
		}
	}
	
	
	@GET
	@Path("/incidentRelations/{instanceId}")
	@Produces({"application/json"})
	public Response getIncidentRelations(
			@PathParam("instanceId") String instanceId,
			@QueryParam("incidentAsJson") String incidentAsJson) throws InvalidDataException, URISyntaxException, JsonSyntaxException, InvalidInstanceException{

		if(incidentAsJson == null || incidentAsJson.isEmpty()){
			return Response.noContent().build();
		}
		
		Gson gson = new GsonBuilder()
			.registerTypeAdapter(GraphRelationMember.class, new GraphRelationMemberJsonAddapter())
			.registerTypeAdapter(GraphElement.class, new GraphElementJsonAddapter())
			.registerTypeAdapter(Calendar.class, new CalendarJsonAddapter())
			.registerTypeAdapter(GregorianCalendar.class, new CalendarJsonAddapter())
			.create();

		Collection<GraphRelation> relations = hnqe
				.getCachTraverseInterface()
				.getIncidentRelations(
						instanceId, 
						gson.fromJson(incidentAsJson, GraphRelationMember.class));
		
		if(relations == null || relations.size() == 0){
			return Response.noContent().build();
		} else {
			
			GraphRelation[] array = new GraphRelation[relations.size()];
			
			return Response.ok(gson.toJson(relations.toArray(array)), "application/json").build();
			
		}
	}
	
	@GET
	@Path("/neighbors/{instanceId}")
	@Produces({"application/json"})
	public Response getNeighbors(
			@PathParam("instanceId") String instanceId,
			@QueryParam("incidentAsJson") String incidentAsJson) throws InvalidDataException, URISyntaxException, JsonSyntaxException, InvalidInstanceException{
		
		if(incidentAsJson == null || incidentAsJson.isEmpty()){
			return Response.noContent().build();
		}
		
		Gson gson = new GsonBuilder()
			.registerTypeAdapter(GraphRelationMember.class, new GraphRelationMemberJsonAddapter())
			.registerTypeAdapter(GraphElement.class, new GraphElementJsonAddapter())
			.registerTypeAdapter(Calendar.class, new CalendarJsonAddapter())
			.registerTypeAdapter(GregorianCalendar.class, new CalendarJsonAddapter())
			.create();

		Collection<GraphRelationMember> relationMembers = hnqe
				.getCachTraverseInterface()
				.getNeighbors(
						instanceId, 
						gson.fromJson(incidentAsJson, GraphRelationMember.class));
		
		if(relationMembers == null || relationMembers.size() == 0){
			return Response.noContent().build();
		} else {
			
			GraphRelationMember[] array = new GraphRelationMember[relationMembers.size()];
			
			return Response.ok(gson.toJson(relationMembers.toArray(array)), "application/json").build();
			
		}
	}
	
	
	@GET
	@Path("/findRelation/{instanceId}")
	@Produces({"application/json"})
	public Response findRelation(
			@PathParam("instanceId") String instanceId,
			@QueryParam("incidentAsJson") String incidentAsJson) throws InvalidDataException, URISyntaxException, JsonSyntaxException, InvalidInstanceException{
		
		if(incidentAsJson == null || incidentAsJson.isEmpty()){
			return Response.noContent().build();
		}
		
		Gson gson = new GsonBuilder()
			.registerTypeAdapter(GraphRelationMember.class, new GraphRelationMemberJsonAddapter())
			.registerTypeAdapter(GraphElement.class, new GraphElementJsonAddapter())
			.registerTypeAdapter(Calendar.class, new CalendarJsonAddapter())
			.registerTypeAdapter(GregorianCalendar.class, new CalendarJsonAddapter())
			.create();

		List<GraphRelationMember> memberList = new ArrayList<GraphRelationMember>();
		
		for(GraphRelationMember member : gson.fromJson(incidentAsJson, GraphRelationMember[].class)){
			memberList.add(member);
		}
		
		Collection<GraphRelation> toReturn = hnqe.getCachTraverseInterface().findRelation(instanceId, memberList);
		
		if(toReturn == null || toReturn.size() == 0){
			
			return Response.noContent().build();
			
		} else {
			
			GraphRelation[] array = new GraphRelation[toReturn.size()];
			
			return Response.ok(gson.toJson(toReturn.toArray(array)), "application/json").build();
			
		}
	}

	
	@GET
	@Path("/findExactRelation/{instanceId}")
	@Produces({"application/json"})
	public Response findExactRelation(
			@PathParam("instanceId") String instanceId,
			@QueryParam("incidentAsJson") String incidentAsJson) throws InvalidDataException, URISyntaxException, JsonSyntaxException, InvalidInstanceException{
		
		if(incidentAsJson == null || incidentAsJson.isEmpty()){
			return Response.noContent().build();
		}
		
		Gson gson = new GsonBuilder()
			.registerTypeAdapter(GraphRelationMember.class, new GraphRelationMemberJsonAddapter())
			.registerTypeAdapter(GraphElement.class, new GraphElementJsonAddapter())
			.registerTypeAdapter(Calendar.class, new CalendarJsonAddapter())
			.registerTypeAdapter(GregorianCalendar.class, new CalendarJsonAddapter())
			.create();

		List<GraphRelationMember> memberList = new ArrayList<GraphRelationMember>();
		
		for(GraphRelationMember member : gson.fromJson(incidentAsJson, GraphRelationMember[].class)){
			memberList.add(member);
		}
		
		Collection<GraphRelation> toReturn = hnqe.getCachTraverseInterface().findExactRelation(instanceId, memberList);
		
		if(toReturn == null || toReturn.size() == 0){
			
			return Response.noContent().build();
			
		} else {
			
			GraphRelation[] array = new GraphRelation[toReturn.size()];
			
			return Response.ok(gson.toJson(toReturn.toArray(array)), "application/json").build();
			
		}
	}

	
	@GET
	@Path("/findPath/{instanceId}")
	@Produces({"application/json"})
	public Response findExactRelation(
			@PathParam("instanceId") String instanceId,
			@QueryParam("startAsJson") String startAsJson,
			@QueryParam("endAsJson") String endAsJson,
			@QueryParam("pathValidatorId") String pathValidatorId,
			@QueryParam("validatorParamsAsJson") @DefaultValue("") String validatorParams,
			@QueryParam("maxPathLength") String maxPathLength, 
			@QueryParam("searchForAll") @DefaultValue("false") String searchForAll) throws InvalidDataException, URISyntaxException, JsonSyntaxException, InvalidInstanceException{
		
		if(startAsJson == null || startAsJson.isEmpty() ||
			endAsJson == null || endAsJson.isEmpty() ||
			pathValidatorId == null || pathValidatorId.isEmpty() ||
			maxPathLength == null || maxPathLength.isEmpty()){
			
			return Response.noContent().build();
			
		}
		
		Gson gson = new GsonBuilder()
			.registerTypeAdapter(GraphRelationMember.class, new GraphRelationMemberJsonAddapter())
			.registerTypeAdapter(GraphElement.class, new GraphElementJsonAddapter())
			.registerTypeAdapter(Calendar.class, new CalendarJsonAddapter())
			.registerTypeAdapter(GregorianCalendar.class, new CalendarJsonAddapter())
			.create();

		GraphRelationMember start = gson.fromJson(startAsJson, GraphRelationMember.class);
		GraphRelationMember end = gson.fromJson(endAsJson, GraphRelationMember.class);
		
		Object[] params = null;
		try{
			params = gson.fromJson(validatorParams, Object[].class);
		} catch (Exception e) {
			throw new InvalidDataException("Parsing the given validator parameters failed due to: "+e.getMessage());
		}
		
		Collection<GraphPath> toReturn = hnqe.getCachTraverseInterface()
				.findPath(instanceId, start, end, Integer.valueOf(maxPathLength), 
						Boolean.valueOf(searchForAll), pathValidatorId, params);
		
		
		if(toReturn == null || toReturn.size() == 0){
			
			return Response.noContent().build();
			
		} else {
			
			GraphPath[] array = new GraphPath[toReturn.size()];
			
			return Response.ok(gson.toJson(toReturn.toArray(array)), "application/json").build();
			
		}

	}

}
